﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Security;
using System.Diagnostics;
using System.IO;

namespace SystemHelperLibrary.API.InOutStream
{
	[SuppressUnmanagedCodeSecurity]
	public static class ConsoleManager
	{
		private const string Kernel32_DllName = "kernel32.dll";

		[DllImport(Kernel32_DllName)]
		private static extern bool AllocConsole();

		[DllImport(Kernel32_DllName)]
		private static extern bool FreeConsole();

		[DllImport(Kernel32_DllName)]
		private static extern IntPtr GetConsoleWindow();

		[DllImport(Kernel32_DllName)]
		private static extern int GetConsoleOutputCP();

		[DllImport("Kernel32.dll")]
		internal static extern IntPtr GetStdHandle(uint nStdHandle);


		public static bool HasConsole
		{
			get
			{
				return GetConsoleWindow() != IntPtr.Zero;
			}
		}

		/// <summary>
		/// Creates a new console instance if the process is not attached to a console already.
		///</summary>    
		public static void Show()
		{
			//#if DEBUG     
			if (!HasConsole)
			{
				AllocConsole();
				InvalidateOutAndError();
			}
			//#endif   
		}
		/// <summary>  
		/// If the process has a console attached to it, it will be detached and no longer visible. Writing to the System.Console is still possible, but no output will be shown.    
		/// </summary>    
		public static void Hide()
		{
			//#if DEBUG 
			if (HasConsole)
			{
				SetOutAndErrorNull();
				FreeConsole();
			}
			//#endif    
		}
		public static void Toggle()
		{
			if (HasConsole)
			{
				Hide();
			}
			else
			{
				Show();
			}
		}

		static void InvalidateOutAndError()
		{
			Type type = typeof(System.Console);
			System.Reflection.FieldInfo _out = type.GetField("_out", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
			System.Reflection.FieldInfo _error = type.GetField("_error", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
			System.Reflection.MethodInfo _InitializeStdOutError = type.GetMethod("InitializeStdOutError", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
			Debug.Assert(_out != null); Debug.Assert(_error != null);
			Debug.Assert(_InitializeStdOutError != null); _out.SetValue(null, null);
			_error.SetValue(null, null); _InitializeStdOutError.Invoke(null, new object[] { true });
		}

		static void SetOutAndErrorNull()
		{
			Console.SetOut(TextWriter.Null);
			Console.SetError(TextWriter.Null);
		}
	}

	public class ConsoleHelper
	{
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage"), System.Security.SuppressUnmanagedCodeSecurity]
		[DllImport("kernel32.dll", SetLastError = true)]
		[return: MarshalAs(UnmanagedType.Bool)]
		internal static extern bool AllocConsole();


		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage"), System.Security.SuppressUnmanagedCodeSecurity]
		[DllImport("kernel32.dll", SetLastError = true)]
		[return: MarshalAs(UnmanagedType.Bool)]
		internal static extern bool FreeConsole();

		[DllImport("Kernel32")]
		internal static extern bool SetConsoleCtrlHandler(HandlerRoutine Handler, bool Add);

		public enum CtrlTypes
		{
			CTRL_C_EVENT = 0,
			CTRL_BREAK_EVENT,
			CTRL_CLOSE_EVENT,
			CTRL_LOGOFF_EVENT = 5,
			CTRL_SHUTDOWN_EVENT
		}

		public delegate bool HandlerRoutine(CtrlTypes CtrlType);

		public static int Create()
		{
			if (AllocConsole())
				return 0;
			else
				return Marshal.GetLastWin32Error();
		}

		public static int Destroy()
		{
			if (FreeConsole())
				return 0;
			else
				return Marshal.GetLastWin32Error();
		}

		public static void Create2()
		{
			AllocConsole();
			SetConsoleCtrlHandler(new HandlerRoutine(ConsoleCtrlCheck), true);
		}

		private static bool ConsoleCtrlCheck(CtrlTypes ctrlType)
		{
			FreeConsole();
			//Your handle code
			return true;
		}


	}

}
